/***************************************************************************
 *   Copyleft 2006-2007 by												   *
 *	 Iwanicki Krzysztof													   *
 *   <gamma.mini.pw.edu.pl/~iwanickik>									   *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 ***************************************************************************/
#include "./BSplineSurfaceManager.h"
#include "../../resource.h"

/**************************************************************
* Constructors                                                *
***************************************************************/
BSplineSurfaceManager::BSplineSurfaceManager(BSplineCurve* bSpline1, BSplineCurve* bSpline2, 
										   BSplineSurface* surface, Cursor3D* cursor): Cursor3DManager(cursor) {
	mBSpline1 = bSpline1;
	mBSpline2 = bSpline2;
	mObject = surface;
	mState = CURVE1;
	mSelectedPoint.mHoriz = -1;
	mSelectedPoint.mVert = -1;
}

BSplineSurfaceManager::BSplineSurfaceManager(BSplineSurface* surface, Cursor3D* cursor): Cursor3DManager(cursor) {
	mBSpline1 = cNull;
	mBSpline2 = cNull;
	mObject = surface;
	mState = SURFACE;
	mSelectedPoint.mHoriz = -1;
	mSelectedPoint.mVert = -1;
}
    
/**************************************************************
* Operators                                                   *
***************************************************************/


/**************************************************************
* Other helpful functions                                     *
***************************************************************/
cVoid BSplineSurfaceManager::DeselectPoint() {
	if (mState == CURVE1) {
		if (mSelectedPoint.mHoriz != -1) {
			mBSpline1->DeselectPoints();
			mSelectedPoint.mHoriz = -1;
		}
	} else if (mState == CURVE2) {
		if (mSelectedPoint.mHoriz != -1) {
			mBSpline2->DeselectPoints();
			mSelectedPoint.mHoriz = -1;
		}
	} else if (mState == SURFACE && mSelectedPoint.mHoriz != -1 && mSelectedPoint.mVert != -1) {
			((BSplineSurface*) mObject)->DeselectPoints();
			mSelectedPoint.mHoriz = -1;
			mSelectedPoint.mVert = -1;
	}
}

cVoid BSplineSurfaceManager::SelectPoint() {
	if (mState == CURVE1) {
		if (mSelectedPoint.mHoriz != -1) {
			mBSpline1->SelectPoint(mSelectedPoint.mHoriz, PointsSet::PERS_SELECTION);
		}
	} else if (mState == CURVE2) {
		if (mSelectedPoint.mHoriz != -1) {
			mBSpline2->SelectPoint(mSelectedPoint.mHoriz, PointsSet::PERS_SELECTION);
		}
	} else if (mState == SURFACE && mSelectedPoint.mHoriz != -1 && mSelectedPoint.mVert != -1) {
			((BSplineSurface*) mObject)->SelectPoint(mSelectedPoint.mHoriz,
				mSelectedPoint.mVert, Mesh::PERS_SELECTION);
	}
}

cVoid BSplineSurfaceManager::FindPoint(cBool bRemember) {
	BSplineCurve* bSpline = cNull;
	if (mState == CURVE1) {
		bSpline = (BSplineCurve*) mBSpline1;
	} else if (mState == CURVE2) {
		bSpline = (BSplineCurve*) mBSpline2;
	}
	if (bSpline) {
		Point4D p = mCursor->GetWorldPosition();

		for (cInt i = 0; i < bSpline->GetNrOfPoints(); i++) {
			if ((bSpline->GetPoint(i) - p).Abs() <= 10.0f) {
				PointsSet::SELECTION_TYPE eType = bSpline->SelectPoint(i, bRemember);
				if (eType == PointsSet::PERS_SELECTION) {
					if (mSelectedPoint.mHoriz != -1) {
						bSpline->SelectPoint(mSelectedPoint.mHoriz, !bRemember);
					}
					mSelectedPoint.mHoriz = i;
				} else {
					mSelectedPoint.mHoriz = -1;
				}
			} 
		}
	} else if (mState == SURFACE) {
		BSplineSurface* surface = (BSplineSurface*) mObject;
		Point4D p = mCursor->GetWorldPosition();
		for (cInt i = 0; i < surface->GetRowCount(); i++) {
			for (cInt j = 0; j < surface->GetColCount(); j++) {
				if ((surface->GetMeshPoint(i, j) - p).Abs() <= 10.0f) {
					Mesh::SELECTION_TYPE eType = surface->SelectPoint(i, j, bRemember);
					if (eType == Mesh::PERS_SELECTION) {
						if (mSelectedPoint.mHoriz != -1 && mSelectedPoint.mVert != -1) {
							surface->SelectPoint(mSelectedPoint.mHoriz, mSelectedPoint.mVert, !bRemember);
						}
						mSelectedPoint.mHoriz = i;
						mSelectedPoint.mVert = j;
					} else {
						mSelectedPoint.mHoriz = -1;
						mSelectedPoint.mVert = -1;
					}
				} 
			}
		}
	}
}

cVoid BSplineSurfaceManager::FindPoint() {
	BSplineCurve* bSpline = cNull;

	if (mState == CURVE1) {
		bSpline = (BSplineCurve*) mBSpline1;
	} else if (mState == CURVE2) {
		bSpline = (BSplineCurve*) mBSpline2;
	}
	if (bSpline) {
		Point4D p = mCursor->GetWorldPosition();

		for (cInt i = 0; i < bSpline->GetNrOfPoints(); i++) {
			if ((bSpline->GetPoint(i) - p).Abs() <= 10.0f) {
				bSpline->SelectPoint(i, PointsSet::TEMP_SELECTION);
			} else {
				bSpline->SelectPoint(i, PointsSet::NO_SELECTION);
			}
		}
	} else if (mState == SURFACE) {
		BSplineSurface* surface = (BSplineSurface*) mObject;
		Point4D p = mCursor->GetWorldPosition();
		for (cInt i = 0; i < surface->GetRowCount(); i++) {
			for (cInt j = 0; j < surface->GetColCount(); j++) {
				if ((surface->GetMeshPoint(i, j) - p).Abs() <= 10.0f) {		
					surface->SelectPoint(i, j, Mesh::TEMP_SELECTION);
				} else {
					surface->SelectPoint(i, j, Mesh::NO_SELECTION);
				}
			}
		}
	}
}

/*virtual*/ cVoid BSplineSurfaceManager::OnMouseMove(cPoint point, Engine3D* engine) {
	Cursor3DManager::OnMouseMove(point, engine);
	if (mLBDown && mSelectedPoint.mHoriz != -1) {
		if (mState == CURVE1) {
			mBSpline1->ReplacePoint(mCursor->GetWorldPosition(), mSelectedPoint.mHoriz);
		} else if (mState == CURVE2 && mSelectedPoint.mHoriz > 0) {
			mBSpline2->ReplacePoint(mCursor->GetWorldPosition(), mSelectedPoint.mHoriz);
		} else if (mState == SURFACE && mSelectedPoint.mHoriz != -1 && mSelectedPoint.mVert != -1) {
			((BSplineSurface*) mObject)->UpdateMeshPoint(mSelectedPoint.mHoriz, mSelectedPoint.mVert, 
				mCursor->GetWorldPosition());
		}
	} else {
		FindPoint();
	}
}

/*virtual*/ cVoid BSplineSurfaceManager::OnMouseWheel(cInt iDelta, Engine3D* engine) {
	Cursor3DManager::OnMouseWheel(iDelta, engine);
	FindPoint();
}

/*virtual*/ cVoid BSplineSurfaceManager::OnLeftMouseDown(cPoint point, Engine3D* engine) {
	InputManager::OnLeftMouseDown(point, engine);	
}

/*virtual*/ cVoid BSplineSurfaceManager::OnLeftMouseUp(cPoint point, Engine3D* engine) {
	InputManager::OnLeftMouseUp(point, engine);
	FindPoint(true);
}

/*virtual*/ cVoid BSplineSurfaceManager::OnRightMouseDown(cPoint point, Engine3D* engine) {
	InputManager::OnRightMouseDown(point, engine);
	if (mState == CURVE1) {
		mBSpline1->AddPoint(mCursor->GetWorldPosition(), 0);
	} else if (mState == CURVE2) {
		mBSpline2->AddPoint(mCursor->GetWorldPosition(), 0);
	}
}

cVoid BSplineSurfaceManager::PrepareSurface(Engine3D* engine) {
	((BSplineSurface*) mObject)->CreateSurface((AbstractCurve*) mBSpline1, (AbstractCurve*) mBSpline2);
	engine->mScene->RemoveObject(mBSpline1);	
	engine->mScene->RemoveObject(mBSpline2);
	mBSpline1 = cNull;
	mBSpline2 = cNull;
}

/*virtual*/ cVoid BSplineSurfaceManager::OnCommand(HWND hwnd, WPARAM wParam, LPARAM lParam, Engine3D* engine) {
	cUShort cmd = LOWORD(wParam);

	switch(cmd) {
		case ID_DEL_POINT: {
			if (mState == CURVE1) {
				if (mSelectedPoint.mHoriz != -1) {
					mBSpline1->RemovePoint(mSelectedPoint.mHoriz);
					mSelectedPoint.mHoriz = -1;
				}
			} else if (mState == CURVE2) {
				if (mSelectedPoint.mHoriz > 0) {
					mBSpline2->RemovePoint(mSelectedPoint.mHoriz);
					mSelectedPoint.mHoriz = -1;
				}
			}
			break;
		}
		case ID_SURFACE_CURVE1:
			if (mBSpline2 && mBSpline1) { 
				cInt iPointsCount;
				if ((iPointsCount = mBSpline1->GetNrOfPoints()) > 0) {
					mBSpline2->AddPoint(mBSpline1->GetPoint(0), 0);
				}
				mBSpline1->SwitchPolygonVisibility();
				mBSpline1->SwitchDisplayPoints();
				mBSpline1->SwitchPointsVisibility();
				mState = CURVE2;
			}
			break;
		case ID_SURFACE_CURVE2:
			PrepareSurface(engine);
			mState = SURFACE;
			break;
		default:
			OnChangeParams(cmd, engine);
			break;
	}
}

cVoid BSplineSurfaceManager::OnChangeParams(cUShort cmd, Engine3D* engine) {
	switch(cmd) {
		case ID_CTRL_N:
			((BSplineSurface*) mObject)->SwitchControlPointsCoordinatesVisibility();
			break;
		case ID_SHOWHIDEPOINTSCORDS:
			((BSplineSurface*) mObject)->SwitchMeshVisibility();
			break;
		case ID_ELLIPSOID_INCB:
			((BSplineSurface*) mObject)->SwitchControlPointsVisibility();
			break;
		case ID_ELLIPSOID_INCC:
			((BSplineSurface*) mObject)->SwitchSurfaceVisibility();
			break;
		case ID_CTRL_J:
			((BSplineSurface*) mObject)->ChangeU(1);
			break;
		case ID_ALT_J:
			((BSplineSurface*) mObject)->ChangeU(-1);
			break;
		case ID_CTRL_K:
			((BSplineSurface*) mObject)->ChangeV(1);
			break;
		case ID_ALT_K:
			((BSplineSurface*) mObject)->ChangeV(-1);
			break;
		case ID_ELLIPSOID_SHOWBB:
			((BSplineSurface*) mObject)->SwitchPeriodic();
			break;
		default:
			OnTransform(cmd, engine);
			break;
	}
}

cVoid BSplineSurfaceManager::OnTransform(cUShort cmd, Engine3D* engine) {
	switch(cmd) {
		case ID_ROTTXA:
			engine->mScene->Transform(Matrix4D::RotationX(-5.0f));
			break;
		case ID_ROTTXQ:
			engine->mScene->Transform(Matrix4D::RotationX(5.0f));
			break;
		case ID_ROTTYS:
			engine->mScene->Transform(Matrix4D::RotationY(-5.0f));
			break;
		case ID_ROTTYW:
			engine->mScene->Transform(Matrix4D::RotationY(5.0f));
			break;
		case ID_ROTTZD:
			engine->mScene->Transform(Matrix4D::RotationZ(-5.0f));
			break;
		case ID_ROTTZE:
			engine->mScene->Transform(Matrix4D::RotationZ(5.0f));
			break;
		case ID_TRANSIZ:
			engine->mScene->Transform(Matrix4D::Translation(Point4D(0.0f, 0.0f, 10.0f)));
			break;
		case ID_TRANSDZ:
			engine->mScene->Transform(Matrix4D::Translation(Point4D(0.0f, 0.0f, -10.0f)));
			break;
		case ID_DESELECTPOINTS:
			DeselectPoint();
			break;
	}
}